// app.cpp
#include <stdio.h>
#include <string.h>
#include "napi/native_api.h"
#include "napi/native_node_api.h"

struct AsyncCallbackInfo {
    napi_env env;   //保持上下文
    napi_async_work asyncWork;
    napi_deferred deferred;
};

// getAppName对应的C/C++实现函数
napi_value JSGetAppName(napi_env env, napi_callback_info info) {
    napi_deferred deferred;
    napi_value promise;
    // 创建promise
    NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));

    AsyncCallbackInfo* asyncCallbackInfo = new AsyncCallbackInfo {
        .env = env,
        .asyncWork = nullptr,
        .deferred = deferred,
    };

    napi_value resourceName;
    napi_create_string_latin1(env, "GetAppName", NAPI_AUTO_LENGTH, &resourceName);
        //此 API 从 ISO-8859-1 编码的 C 字符串创建一个 JavaScript 对象。复制本机字符串
        // [in] env：调用 API 的环境。
        // [in] str：表示 ISO-8859-1 编码字符串的字符缓冲区。
        // [in] length：字符串的长度（以字节为单位），或者如果字符串以空值终止。NAPI_AUTO_LENGTH
        // [out] result：表示 JavaScript 的 A。napi_valueString

    // 创建异步任务队列
    napi_create_async_work(
        //函数分配一个工作对象，我们可以在其中指定工作处理函数
        // [in] env：调用 API 的环境。
        // [in] async_resource：与异步工作关联的可选对象，该对象将传递给可能的 init 挂接。async_hooks
        // [in] async_resource_name：为 API 公开的诊断信息提供的资源类型的标识符。async_hooks
        // [in] execute：应调用以异步执行逻辑的本机函数。给定的函数从工作线程池线程调用，可以与主事件循环线程并行执行。
        // [in] complete：异步逻辑完成或取消时调用的本机函数。给定的函数是从主事件循环线程调用的。napi_async_complete_callback提供了更多详细信息。
        // [in] data：用户提供的数据上下文。这将传递回执行和完成函数。
        // [out] result：这是新创建的异步工作的句柄。napi_async_work*
        // 如果 API 成功，则返回napi_ok
        // 此 API 分配一个用于异步执行逻辑的工作对象。一旦不再需要工作，就应该使用napi_delete_async_work释放它。

        env, nullptr, resourceName,
        // 异步任务的回调
        [](napi_env env, void* data) {},
        // 异步任务结束后的回调
        [](napi_env env, napi_status status, void* data) {
            AsyncCallbackInfo* asyncCallbackInfo = (AsyncCallbackInfo*)data;
            napi_value appName;
            const char* str = "com.example.helloworld";
            napi_create_string_utf8(env, str, strlen(str), &appName);
            // 触发回调
            napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, appName);
                // [in] env：调用 API 的环境。
                // [in] deferred：要解析其关联承诺的延迟对象。
                // [in] resolution：用于解析承诺的值。
                // 此 API 通过与 JavaScript 承诺关联的延迟对象来解析 JavaScript 承诺。因此，它只能用于解析相应的延迟对象可用的 JavaScript 承诺。这实际上意味着必须使用 创建承诺，并且必须保留从该调用返回的延迟对象才能传递到此 API。napi_create_promise()
            napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
                // [in] env：调用 API 的环境。
                // [in] work：调用 返回的句柄。napi_create_async_work
                // 如果 API 成功，则返回。napi_ok
                // 此 API 释放以前分配的工作对象。napi_create_promise()
            delete asyncCallbackInfo;
        },
        (void*)asyncCallbackInfo, &asyncCallbackInfo->asyncWork);

    napi_queue_async_work(env, asyncCallbackInfo->asyncWork);
        // [in] env：调用 API 的环境。
        // [in] work：调用 返回的句柄。napi_create_async_work
        // 如果 API 成功，则返回。napi_ok

        // 此 API 释放以前分配的工作对象。
    return promise;
}

// 模块导出入口函数
static napi_value AppExport(napi_env env, napi_value exports)
{
    static napi_property_descriptor desc[] = {
        // utf8name：描述属性键的可选，编码为 UTF8。酒店必须提供其中之一，或必须为酒店提供。Stringutf8namename
        // name：指向要用作属性键的 JavaScript 字符串或符号的可选。酒店必须提供其中之一，或必须为酒店提供。napi_valueutf8namename
        // value：如果属性是数据属性，则由属性的 get 访问权限检索的值。如果将其传入，则设置 、 和 to（因为这些成员不会被使用）。gettersettermethoddataNULL
        // getter：执行属性的获取访问权限时要调用的函数。如果将其传入，则设置和到（因为这些成员将不会被使用）。当从 JavaScript 代码访问属性时（或者如果使用 N-API 调用对属性执行 get），运行时会隐式调用给定的函数。napi_callback提供了更多详细信息。valuemethodNULL
        // setter：执行属性的设置访问时要调用的函数。如果将其传入，则设置和到（因为这些成员将不会被使用）。当从 JavaScript 代码设置属性时（或者如果使用 N-API 调用执行属性上的设置），则运行时会隐式调用给定的函数。napi_callback提供了更多详细信息。valuemethodNULL
        // method：设置此选项可使属性描述符对象的属性成为 由 表示的 JavaScript 函数。如果将其传入，则 set 和 to（因为这些成员不会被使用）。napi_callback提供了更多详细信息。valuemethodvaluegettersetterNULL
        // attributes：与特定属性关联的属性。请参阅napi_property_attributes。
        // data：传入的回调数据，以及是否调用此函数。methodgettersetter
        DECLARE_NAPI_FUNCTION("getAppName", JSGetAppName),
    };
    NAPI_CALL(env,
        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)
            // [in] env：调用 N-API 调用的环境。
            // [in] object：要从中检索属性的对象。
            // [in] property_count：数组中的元素数。properties
            // [in] properties：属性描述符的数组。
            // 如果 API 成功，则返回。napi_ok
            // 此方法允许在给定对象上有效地定义多个属性。这些属性是使用属性描述符定义的（请参见napi_property_descriptor）。给定一个此类属性描述符数组，此 API 将一次设置一个对象上的属性，如 （ECMA-262 规范的第 9.1.6 节所述）。DefineOwnProperty()
    );
    //用户提供的本机函数的函数指针类型，这些函数将通过 N-API 公开给 JavaScript
    return exports;
}

// app模块描述
static napi_module appModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = AppExport,
    .nm_modname = "app",
    .nm_priv = ((void*)0),
    .reserved = {0}
};

// 注册模块
extern "C" __attribute__((constructor)) void AppRegister()
{
    napi_module_register(&appModule);
}
